package websocket_plugin

import (
	"context"
	"github.com/gorilla/websocket"
	"net/http"
	"time"
)

type wsHelper struct {
	WebsocketConfig
	*Session
	WebsocketHandler
}

func newWsHelper(websocketConfig WebsocketConfig, handler WebsocketHandler) *wsHelper {
	return &wsHelper{WebsocketConfig: websocketConfig, WebsocketHandler: handler}
}

func (w *wsHelper) serve(r *http.Request, rw http.ResponseWriter) {
	session, err := w.initialize(r, rw)
	w.Session = session
	if err != nil {
		w.OnError(session, err)
		return
	}
	defer session.Close()
	defer w.OnClose(session)
	ctx, cancel := context.WithCancel(r.Context())
	defer cancel()
	session.Context = ctx
	go w.ping()
	err = w.listen()
	if err != nil {
		w.OnError(session, err)
	}
}

func (w *wsHelper) initialize(r *http.Request, rw http.ResponseWriter) (*Session, error) {
	ug := websocket.Upgrader{}
	ug.CheckOrigin = func(r *http.Request) bool {
		return !w.CheckOrigin
	}
	conn, err := ug.Upgrade(rw, r, nil)
	if err != nil {
		return nil, err
	}
	session := newSession(conn, r)
	w.OnOpen(session)
	return session, nil
}

func (w *wsHelper) listen() error {
	w.SetReadLimit(w.MaxMessageSize)
	w.SetReadDeadline(time.Now().Add(w.PongWait))
	w.SetPongHandler(func(string) error {
		w.SetReadDeadline(time.Now().Add(w.PongWait))
		return nil
	})
	for {
		select {
		case <-w.Context.Done():
			return nil
		default:
			_, message, err := w.ReadMessage()
			if err != nil {
				return err
			}
			err = w.OnMessage(w.Session, message)
			if err != nil {
				return err
			}
		}
	}
}

func (w *wsHelper) ping() {
	ticker := time.NewTicker(w.PingPeriod)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			if err := w.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(w.WriteWait)); err != nil {
				return
			}
		default:
		}
	}
}
